package net.aiion.weave.impl.cards.expressions;

import net.aiion.weave.spi.cards.CardException;
import net.aiion.weave.spi.cards.expressions.CardExpressionTokenizerAction;
import net.aiion.weave.spi.cards.expressions.CardExpressionTokenizerService;
import net.aiion.weave.spi.naming.annotations.Named;
import net.aiion.weave.spi.naming.annotations.UserScoped;

@UserScoped
@Named
public class CardExpressionTokenizerServiceImpl implements CardExpressionTokenizerService
{
    private static final int STATE_NORMAL = 0;
    private static final int STATE_DOLLAR = 1;
    private static final int STATE_LEFT_BRACE = 2;
    private static final int STATE_COLON = 3;

    public CardExpressionTokenizerServiceImpl() {
        // TODO Gedanken machen, wie dann mit den Tokens im UIBinder verfahren wird (siehe ComponentCards)
    }

    @Override
    public void tokenizeExpressions(final String aInputString,
        final CardExpressionTokenizerAction aCardExpressionTokenizerAction)
    {
        if (aInputString.indexOf('$') == -1) {
            aCardExpressionTokenizerAction.handleTextToken(aInputString);
            return;
        }

        final StringBuilder resultBuilder = new StringBuilder();
        final StringBuilder expressionPrefixBuilder = new StringBuilder();
        final StringBuilder expressionValueBuilder = new StringBuilder();

        final char[] inputArray = aInputString.toCharArray();
        final int inputLength = aInputString.length();

        int currentPosition = 0;
        int currentState = STATE_NORMAL;

        while (currentPosition < inputLength) {
            final char c = inputArray[currentPosition++];

            switch (c) {
            case '$':
                currentState = resolveExpressionsHandleDollar(currentState, resultBuilder, expressionPrefixBuilder,
                    expressionValueBuilder);
                break;
            case '{':
                currentState = resolveExpressionsHandleLeftBraces(currentState, resultBuilder, expressionPrefixBuilder,
                    expressionValueBuilder);
                break;
            case ':':
                currentState = resolveExpressionsHandleColon(currentState, resultBuilder, expressionPrefixBuilder,
                    expressionValueBuilder);
                break;
            case '}':
                currentState = resolveExpressionsHandleRightBraces(currentState, resultBuilder,
                    expressionPrefixBuilder, expressionValueBuilder, aCardExpressionTokenizerAction);
                break;
            default:
                currentState = resolveExpressionsHandleDefault(currentState, resultBuilder, expressionPrefixBuilder,
                    expressionValueBuilder, c);
                break;
            }
        }

        if (resultBuilder.length() > 0) {
            aCardExpressionTokenizerAction.handleTextToken(resultBuilder.toString());
        }
    }

    private int resolveExpressionsHandleDollar(final int aState, final StringBuilder aResultBuilder,
        final StringBuilder aExpressionPrefixBuilder, final StringBuilder aExpressionValueBuilder)
    {
        switch (aState) {
        case STATE_NORMAL:
            return STATE_DOLLAR;
        case STATE_DOLLAR:
            aResultBuilder.append('$');
            return STATE_DOLLAR;
        case STATE_LEFT_BRACE:
            aExpressionPrefixBuilder.append('$');
            return STATE_LEFT_BRACE;
        case STATE_COLON:
            aExpressionValueBuilder.append('$');
            return STATE_COLON;
        default:
            throw new CardException("Invalid state.");
        }
    }

    private int resolveExpressionsHandleLeftBraces(final int aState, final StringBuilder aResultBuilder,
        final StringBuilder aExpressionPrefixBuilder, final StringBuilder aExpressionValueBuilder)
    {
        switch (aState) {
        case STATE_NORMAL:
            aResultBuilder.append('{');
            return STATE_NORMAL;
        case STATE_DOLLAR:
            return STATE_LEFT_BRACE;
        case STATE_LEFT_BRACE:
            aExpressionPrefixBuilder.append('{');
            return STATE_LEFT_BRACE;
        case STATE_COLON:
            aExpressionValueBuilder.append('{');
            return STATE_COLON;
        default:
            throw new CardException("Invalid state.");
        }
    }

    private int resolveExpressionsHandleColon(final int aState, final StringBuilder aResultBuilder,
        final StringBuilder aExpressionPrefixBuilder, final StringBuilder aExpressionValueBuilder)
    {
        switch (aState) {
        case STATE_NORMAL:
            aResultBuilder.append(':');
            return STATE_NORMAL;
        case STATE_DOLLAR:
            aResultBuilder.append("$:");
            return STATE_NORMAL;
        case STATE_LEFT_BRACE:
            return STATE_COLON;
        case STATE_COLON:
            aExpressionValueBuilder.append(':');
            return STATE_COLON;
        default:
            throw new CardException("Invalid state.");
        }
    }

    private int resolveExpressionsHandleRightBraces(final int aState, final StringBuilder aResultBuilder,
        final StringBuilder aExpressionPrefixBuilder, final StringBuilder aExpressionValueBuilder,
        final CardExpressionTokenizerAction aCardExpressionTokenizerAction)
    {
        switch (aState) {
        case STATE_NORMAL:
            aResultBuilder.append('}');
            return STATE_NORMAL;
        case STATE_DOLLAR:
            aResultBuilder.append("$}");
            return STATE_NORMAL;
        case STATE_LEFT_BRACE:
            if (aResultBuilder.length() > 0) {
                aCardExpressionTokenizerAction.handleTextToken(aResultBuilder.toString());
            }

            aCardExpressionTokenizerAction.handleExpressionToken("", aExpressionPrefixBuilder.toString());
            aResultBuilder.setLength(0);
            aExpressionPrefixBuilder.setLength(0);
            return STATE_NORMAL;
        case STATE_COLON:
            if (aResultBuilder.length() > 0) {
                aCardExpressionTokenizerAction.handleTextToken(aResultBuilder.toString());
            }

            aCardExpressionTokenizerAction.handleExpressionToken(aExpressionPrefixBuilder.toString(),
                aExpressionValueBuilder.toString());
            aResultBuilder.setLength(0);
            aExpressionPrefixBuilder.setLength(0);
            aExpressionValueBuilder.setLength(0);
            return STATE_NORMAL;
        default:
            throw new CardException("Invalid state.");
        }
    }

    private int
        resolveExpressionsHandleDefault(final int aState, final StringBuilder aResultBuilder,
            final StringBuilder aExpressionPrefixBuilder, final StringBuilder aExpressionValueBuilder,
            final char aCharacter)
    {
        switch (aState) {
        case STATE_NORMAL:
            aResultBuilder.append(aCharacter);
            return STATE_NORMAL;
        case STATE_DOLLAR:
            aResultBuilder.append(aCharacter);
            return STATE_NORMAL;
        case STATE_LEFT_BRACE:
            aExpressionPrefixBuilder.append(aCharacter);
            return STATE_LEFT_BRACE;
        case STATE_COLON:
            aExpressionValueBuilder.append(aCharacter);
            return STATE_COLON;
        default:
            throw new CardException("Invalid state.");
        }
    }
}
